前幾篇文章提到了BDD的觀念,以及在.NET solution中,簡單的介紹了如何透過SpecFlow這個工具,來幫助我們減少在驗收測試案例與開始進行TDD中間的落差。
這篇文章則針對BDD的觀念,做個回顧與總結,希望讓讀者朋友們可以清楚的知道,怎麼運用BDD,來產生TDD一開始所需要的測試案例。
上一篇文章:[Day 25]BDD - TDD from BDD
本系列文章專區
@開發流程
先回顧一下前面文章:[Day 22]ATDD - ATDD的循環所提到的ATDD循環圖:
廢話不多說,這邊筆者直接透過連續的幾張圖,來幫助讀者瞭解,從一開始定義使用者需求開始,一路到最後通過所有測試,整個流程與彼此之間的對應關係。
並且遵守著本系列文最重要的宗旨:一切都是為了滿足使用者需求。
@定義使用者需求
整個ATDD的流程,是先從「撰寫user story」或是「挑選一個user story」開始的,如下圖所示:
所以,首先先透過user story來定義使用者需求:
範例:
@Acceptance Test Cases 與 BDD Feature
如下圖所示:
範例如下圖所示:
@Scenario
如同user story與acceptance test cases的關係,BDD透過scenario來代表這個feature應該要滿足哪些情境。
因此我們可以參考acceptance test cases,來擬定對應的scenario。如下圖所示:
範例如下圖所示:
@Given, When, Then
在BDD的scenario中,運用三個關鍵字來描述scenario,分別是:
如下圖所示:
有了這三個關鍵字所形成的description,基本上就可以透成描述一個scenario的基本要素。
@對應Unit Testing的3A原則
3A原則可以參考前面的文章:[Day 3]動手寫Unit Test
Unit Testing的3A原則對應到BDD scenario的關係如下:
如下圖所示:
當我們已經透過BDD的工具(如SpecFlow),定義好feature與scenario,此時BDD library會自動幫我們建好了按照scenario的描述建立好scenario(其實就是測試案例)執行的flow。
接著只需要在scenario的三個關鍵字,對應到unit testing 3A原則,依照scenario的描述,去填入我們的TDD測試案例即可。
這時,我們TDD的測試案例與測試程式,已經不是單純的一個方法,而是一個對使用者來說,有意義的scenario,這個scenario是為了滿足使用者需求,所對應的user story,所break down出來的acceptance test cases。
透過使用DSL描述的scenario,將使用者需求,與TDD所要撰寫的系統程式碼連接在一起。
@TDD的紅燈
這時,我們已經清楚的定義好哪一些測試案例通過後,即代表滿足了使用者需求。
目前應該只有目標測試物件,可能也透過IDE工具,幫我們迅速建立好空的類別、屬性與方法。
因此,此時執行測試,應該會得到一堆測試失敗的結果。也就是TDD的紅燈。如下圖所示:
這個紅燈的意義,檯面上雖是測試失敗,但其本質意義則是:目標!
這個目標,是完全符合使用者需求的,接著開發人員在撰寫、設計程式碼時,就可以心無旁騖,一心專注的滿足這個目標,也就是通過測試即可。
這正是TDD美妙之處:
@撰寫Production code,只為了通過測試
接下來開發人員只要專心的滿足測試案例,即代表滿足使用者需求。
換句話說,接下來是依照測試案例來撰寫系統的production code。如下圖所示:
這邊會有一個開發人員一開始無法突破的障礙:
「只滿足測試案例,就代表系統功能完整完成了嗎?不是吧?!要滿足測試案例的方法這麼多,但寫出來的code,有時甚至根本就不是系統功能要的,例如寫死hard-code的回傳結果,這能稱的上是一個好的系統、正常運作的系統嗎?」
這幾乎是每個開發人員從TAD(Test-After Development)轉換到TDD會碰到的門檻。
答案是:「對!只要滿足測試案例,就代表這個功能符合需求,這個功能可以正常運作」,但前提是:「測試案例夠完整,要能涵蓋到這個需求所有代表性的scenario」。
開發人員會有這樣的門檻,是因為有一個盲點:「切入角度是程式碼,而非用測試案例來代表使用者需求」。
事實上,每一行程式碼,每一個功能所存在的目的,就是為了滿足使用者需求,而使用者需求在寫程式前,已經被轉換成了測試案例。也就是程式碼只需要滿足測試案例、通過測試即可。
若有需求異動,代表需要新增或修改測試案例。
若有行為上的bug、defect,則代表測試案例涵蓋的不夠完整。
整套的TDD,會將測試案例的重要性拉到相當高,因為測試案例就代表著每個角色的共識,也是系統的最高目的:使用者的需求。
@使Production code通過TDD的測試案例
當Production code通過了TDD的測試案例,即代表滿足了該scenario。如下圖所示:
@滿足了Scenario,代表滿足了驗收測試案例
當scenario一一滿足,即代表一一滿足了驗收測試案例。因為在一開始設計scenario的時候,就是依據acceptance test cases而設計的。如下圖所示:
@滿足所有驗收測試案例,即代表滿足了user story
當所有驗收測試案例都通過了,就代表這個user story被完成了,也就代表這個使用者需求被完成了。如下圖所示:
基本上整個流程就如同一開始那張圖所示:
也就是下面這一系列的步驟:
當眼前這個user story完成後,接著挑下一個user story,進入下一個ATDD/BDD/TDD的循環。
整個軟體開發的流程,也就如同前面所提到的W-model,如下圖所示:
註:每一段production code通過測試後,千萬別忘了還有個重要的步驟,叫做「重構」。重構只需滿足物件導向設計的原則,就相當足夠了。請參考前面 Day9~Day19 的文章。
@結論
本系列文章,從一開始到現在,總算把整套TDD所需用到的拼圖,都一一介紹完畢了。
TDD絕對不是只有「先寫測試」這麼單純、簡單。只有「先寫測試」,也完全無法體會到TDD的好處,反而容易造成開發人員認為:「為什麼我要多寫一份或多份程式,來驗證我已經知道的東西?」、「production code是我寫的,為什麼還要由我自己來驗證我自己的code對不對?」
如果TDD只是按照開發人員心中所想的系統,來先寫測試,那over design的問題仍在,仍可能寫出一堆華麗而無用的東西,仍可能累的要死寫出很讚的功能後,卻不是使用者要的。
另外,TDD的一個盲點就是,倘若一開始的切入角度都只在單元測試,那在實務應用上肯定會碰到問題,例如:TDD的測試案例不知道怎麼來、覺得測試程式是無用且多餘的、覺得TDD或測試程式一點都不實用。
那是因為TDD若著重在單元測試,則測試案例需要由開發人員自己發想,而自己想的測試案例,就跟自己寫的production code沒啥兩樣,那感覺就真的很多餘。
倘若TDD的測試案例,是由使用者與測試人員所定義,並取得開發人員共識後,由領域語言所描述,對應到TDD的測試案例,那麼這個測試案例的意義將完全不一樣,對開發人員來說,他也不是一份多餘無意義的工,而是定義出明確的目標,開發人員「只需要」滿足測試案例即可。
由於這一整套的TDD process,需要各角色的協同合作,因此強烈建議各開發團隊可以參考Scrum的流程,因為Scrum的角色、階段、開發進行方式,跟TDD的開發方式幾近於完全吻合。
而TDD被定義在extreme programming(XP)中,也不是完全獨立的,extreme programming其他的feature,正滿足了Agile的精神,整個XP的infrastructure,正是整個Scrum流程中,開發人員的運作引擎。
重視溝通、榮辱共享、擁抱變化、迅速回饋,目的只有一個:滿足使用者需求。
因此,任何需求的變化,都是軟體成長與演進的一個動力。Be a team,讓使用者或代表使用者的PO(Product Owner),也是團隊的一份子。整個團隊盡力support PO,但PO也是團隊的一份子,所以產品delay,也是大家的責任,尤其是PO要負最大責任。
這一整套開發方式,牽涉到開發流程(Scrum)、基礎建設(XP)、開發軟體精神(Agile),以及工程師的基本功力(OOD/OOP)。雖非一蹴可幾,但相關人員先將每一個拼圖都先學好、練好之後,最後只需要policy支持,再將每一塊拼圖拼起來,TDD真的沒這麼難,也沒這麼遙不可及,更不是個神話或烏托邦的世界。
(如果,連我們身在其中都不去瞭解,又怎麼讓manager或公司支持呢?又怎麼有資格來評論或批評,這樣的方法論一無是處或是空口說白話呢?)
hatelove提到:
倘若TDD的測試案例,是由使用者與測試人員所定義,並取得開發人員共識後,由領域語言所描述,對應到TDD的測試案例,那麼這個測試案例的意義將完全不一樣,對開發人員來說,他也不是一份多餘無意義的工,而是定義出明確的目標,開發人員「只需要」滿足測試案例即可。
這或許是整件事是否能成局的關鍵之一
很希望有機會實際應用這種方式
看文中介紹的 SpecFlow 似乎只支援 .Net(Visaul Studio)
順著官網超連結逛到了支援多種程式語言的 Cucumber
看到他的介紹,不禁莞爾
上面是這麼寫的
1: Describe behaviour in plain text
2: Write a step definition in Ruby
3: Run and watch it fail
4. Write code to make the step pass
5. Run again and see the step pass
6. Repeat 2-5 until green like a cuke
7. Repeat 1-6 until the money runs out
恩恩,各語言通常都有類似的BDD library,您提到的那個黃瓜,我也覺得很讚 XD
真是精采!!!!!太贊了
剛剛 SpecFlow 一直都裝不起來,不過無意間發現 Windows Phone 也有BDD工具~~等等來試試看好了...
其實,BDD的原理真的也沒這麼難,簡單的說,就是把.feature檔,透過T4自動長成對應的測試程式。所以config中設定的unitTestProvider,就是選擇要用哪一個T4 template。
不過,在scenario上,設定中斷點,可以進去對應的程式碼裡面,這招就真的很酷...